AWS Cloud Development Kit(CDK)(Python)を使ってVPC+Sagemaker+Lambda環境構築してみた(VPC編)
どーもsutoです。
私は業務のなかでCloudFormationを使ってインフラ構築を自動化しているのですが、今回はAWS Cloud Development Kit(CDK)を使ってPythonコードでインフラ構築自動化をやってみたいと思います。
AWS CDKは、CloudFormationのテンプレートをTypescriptやPythonのようなプログラミング言語で記述でき、コマンド1つでデプロイ、破棄、スタック更新のDiffができるツールです。
はじめに
今回、自分のアカウントで機械学習検証用環境のインフラを構築し、いつでも環境のデプロイ/破棄ができるように自動化&コード管理したい、というモチベからAWS CDKを本格的に触ってみることにしました。
本検証の目標
- AWS CDKのインストールと初期設定、VPC構築(←本記事)
- Sagemakerのノートブックインスタンスを自動構築、VPCとの関連付け
- ノートブックインスタンスのスケジュールによる自動停止を行うLambdaの追加設定
でやっていきます。
2、3の内容は後ほど別の記事でまとめていきます。
AWS CDKのインストール
ローカル作業環境はMacでPython3インストール済みです。
まずnpmでインストールします
npm install -g aws-cdk cdk --version
ローカル環境での下準備
CDKの新しいプロジェクト用のフォルダを作成し、移動します。
mkdir sage-nw && cd sage-nw
使用言語を指定してプロジェクトの雛形を作成します。
~ sage-nw % cdk init --language python ~ sage-nw % ls README.md app.py cdk.json requirements.txt sage_nw setup.py source.bat
ディレクトリ内の仮想環境に入ります。
~ sage-nw % source .env/bin/activate (.env) ~ sage-nw %
setup.py
の「install_requires」部分を編集(aws-cdk.aws-ec2追記)して必要なリソースのモジュールもインストールします。
install_requires=[ "aws-cdk.core", "aws-cdk.aws-ec2", ],
pip install -r requirements.txt
app.py
を編集してデプロイするリージョンを指定しておくと便利です。
SageNwStack(app, "sage-nw", env={'region': 'ap-northeast-1'})
コードを記述
ここから「sage_nw」フォルダ内のsage_nw_stack.py
にAWSリソースの構築内容を記述して保存します。
今回のVPC構築内容は以下のとおりです。
- CIDRが/16のVPCを1つ新規作成
- CIDRが/24のパブリックサブネット2つ、プライベートサブネット2つを各AZに1つずつ配置されるように新規作成
- セキュリティグループを1つ新規作成(sagemaker ノートブックにアタッチする用に作っておきます)
from aws_cdk import core from aws_cdk import aws_ec2 as ec2 class SageNwStack(core.Stack): def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # The code that defines your stack goes here # 変数の宣言 vpc_cidr = '10.1.0.0/16' subnet_mask = 24 # 新規VPC作成 vpc = ec2.Vpc( self, id="Sage-vpc", cidr=vpc_cidr, nat_gateways=0, subnet_configuration=[ ec2.SubnetConfiguration( cidr_mask=subnet_mask, name='public', subnet_type=ec2.SubnetType.PUBLIC, ), ec2.SubnetConfiguration( cidr_mask=subnet_mask, name='private', subnet_type=ec2.SubnetType.ISOLATED, ), ], ) security_group = ec2.SecurityGroup( self, id='Sage-sg', vpc=vpc, security_group_name='test-sg' ) security_group.add_ingress_rule( peer=ec2.Peer.ipv4(vpc_cidr), connection=ec2.Port.all_traffic(), )
デフォルト設定でVPCを構築すると、サブネットのCIDRが勝手に決められる、NATゲートウェイが作成されるなど少々都合が悪かったのでいろいろオプションを指定しています。
参考
スタックの確認とデプロイ
lsコマンドで本プロジェクトで作成されるスタック一覧を表示できます。
cdk ls
cdk synth
コマンドでCloudFormationテンプレで記述した場合のコードを見ることができます。
Resources: SagevpcFE72F9E6: Type: AWS::EC2::VPC Properties: CidrBlock: 10.1.0.0/16 EnableDnsHostnames: true EnableDnsSupport: true InstanceTenancy: default Tags: - Key: Name Value: sage-nw/Sage-vpc Metadata: aws:cdk:path: sage-nw/Sage-vpc/Resource SagevpcpublicSubnet1Subnet94477DD3: Type: AWS::EC2::Subnet Properties: CidrBlock: 10.1.0.0/24 VpcId: Ref: SagevpcFE72F9E6 AvailabilityZone: Fn::Select: - 0 - Fn::GetAZs: "" MapPublicIpOnLaunch: true Tags: - Key: aws-cdk:subnet-name Value: public - Key: aws-cdk:subnet-type Value: Public - Key: Name Value: sage-nw/Sage-vpc/publicSubnet1 Metadata: aws:cdk:path: sage-nw/Sage-vpc/publicSubnet1/Subnet SagevpcpublicSubnet1RouteTableC299A029: Type: AWS::EC2::RouteTable Properties: VpcId: Ref: SagevpcFE72F9E6 Tags: - Key: Name Value: sage-nw/Sage-vpc/publicSubnet1 Metadata: aws:cdk:path: sage-nw/Sage-vpc/publicSubnet1/RouteTable SagevpcpublicSubnet1RouteTableAssociation6D1A442B: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: Ref: SagevpcpublicSubnet1RouteTableC299A029 SubnetId: Ref: SagevpcpublicSubnet1Subnet94477DD3 Metadata: aws:cdk:path: sage-nw/Sage-vpc/publicSubnet1/RouteTableAssociation SagevpcpublicSubnet1DefaultRoute07A07172: Type: AWS::EC2::Route Properties: RouteTableId: Ref: SagevpcpublicSubnet1RouteTableC299A029 DestinationCidrBlock: 0.0.0.0/0 GatewayId: Ref: SagevpcIGW9475FA95 DependsOn: - SagevpcVPCGW7B030D8E Metadata: aws:cdk:path: sage-nw/Sage-vpc/publicSubnet1/DefaultRoute SagevpcpublicSubnet2Subnet2C67E71A: Type: AWS::EC2::Subnet Properties: CidrBlock: 10.1.1.0/24 VpcId: Ref: SagevpcFE72F9E6 AvailabilityZone: Fn::Select: - 1 - Fn::GetAZs: "" MapPublicIpOnLaunch: true Tags: - Key: aws-cdk:subnet-name Value: public - Key: aws-cdk:subnet-type Value: Public - Key: Name Value: sage-nw/Sage-vpc/publicSubnet2 Metadata: aws:cdk:path: sage-nw/Sage-vpc/publicSubnet2/Subnet SagevpcpublicSubnet2RouteTable8690CC41: Type: AWS::EC2::RouteTable Properties: VpcId: Ref: SagevpcFE72F9E6 Tags: - Key: Name Value: sage-nw/Sage-vpc/publicSubnet2 Metadata: aws:cdk:path: sage-nw/Sage-vpc/publicSubnet2/RouteTable SagevpcpublicSubnet2RouteTableAssociation3C69FE0F: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: Ref: SagevpcpublicSubnet2RouteTable8690CC41 SubnetId: Ref: SagevpcpublicSubnet2Subnet2C67E71A Metadata: aws:cdk:path: sage-nw/Sage-vpc/publicSubnet2/RouteTableAssociation SagevpcpublicSubnet2DefaultRouteE00663C9: Type: AWS::EC2::Route Properties: RouteTableId: Ref: SagevpcpublicSubnet2RouteTable8690CC41 DestinationCidrBlock: 0.0.0.0/0 GatewayId: Ref: SagevpcIGW9475FA95 DependsOn: - SagevpcVPCGW7B030D8E Metadata: aws:cdk:path: sage-nw/Sage-vpc/publicSubnet2/DefaultRoute SagevpcprivateSubnet1SubnetB83A6331: Type: AWS::EC2::Subnet Properties: CidrBlock: 10.1.2.0/24 VpcId: Ref: SagevpcFE72F9E6 AvailabilityZone: Fn::Select: - 0 - Fn::GetAZs: "" MapPublicIpOnLaunch: false Tags: - Key: aws-cdk:subnet-name Value: private - Key: aws-cdk:subnet-type Value: Isolated - Key: Name Value: sage-nw/Sage-vpc/privateSubnet1 Metadata: aws:cdk:path: sage-nw/Sage-vpc/privateSubnet1/Subnet SagevpcprivateSubnet1RouteTable1F39D14D: Type: AWS::EC2::RouteTable Properties: VpcId: Ref: SagevpcFE72F9E6 Tags: - Key: Name Value: sage-nw/Sage-vpc/privateSubnet1 Metadata: aws:cdk:path: sage-nw/Sage-vpc/privateSubnet1/RouteTable SagevpcprivateSubnet1RouteTableAssociation385BE067: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: Ref: SagevpcprivateSubnet1RouteTable1F39D14D SubnetId: Ref: SagevpcprivateSubnet1SubnetB83A6331 Metadata: aws:cdk:path: sage-nw/Sage-vpc/privateSubnet1/RouteTableAssociation SagevpcprivateSubnet2Subnet6652904B: Type: AWS::EC2::Subnet Properties: CidrBlock: 10.1.3.0/24 VpcId: Ref: SagevpcFE72F9E6 AvailabilityZone: Fn::Select: - 1 - Fn::GetAZs: "" MapPublicIpOnLaunch: false Tags: - Key: aws-cdk:subnet-name Value: private - Key: aws-cdk:subnet-type Value: Isolated - Key: Name Value: sage-nw/Sage-vpc/privateSubnet2 Metadata: aws:cdk:path: sage-nw/Sage-vpc/privateSubnet2/Subnet SagevpcprivateSubnet2RouteTableB1CE60A7: Type: AWS::EC2::RouteTable Properties: VpcId: Ref: SagevpcFE72F9E6 Tags: - Key: Name Value: sage-nw/Sage-vpc/privateSubnet2 Metadata: aws:cdk:path: sage-nw/Sage-vpc/privateSubnet2/RouteTable SagevpcprivateSubnet2RouteTableAssociationD4FFD8F0: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: Ref: SagevpcprivateSubnet2RouteTableB1CE60A7 SubnetId: Ref: SagevpcprivateSubnet2Subnet6652904B Metadata: aws:cdk:path: sage-nw/Sage-vpc/privateSubnet2/RouteTableAssociation SagevpcIGW9475FA95: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: sage-nw/Sage-vpc Metadata: aws:cdk:path: sage-nw/Sage-vpc/IGW SagevpcVPCGW7B030D8E: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: Ref: SagevpcFE72F9E6 InternetGatewayId: Ref: SagevpcIGW9475FA95 Metadata: aws:cdk:path: sage-nw/Sage-vpc/VPCGW SagesgCC54FEB9: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: sage-nw/Sage-sg GroupName: test-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 Description: Allow all outbound traffic by default IpProtocol: "-1" SecurityGroupIngress: - CidrIp: 10.1.0.0/16 Description: from 10.1.0.0/16:ALL TRAFFIC IpProtocol: "-1" VpcId: Ref: SagevpcFE72F9E6 Metadata: aws:cdk:path: sage-nw/Sage-sg/Resource CDKMetadata: Type: AWS::CDK::Metadata Properties: Modules: aws-cdk=1.54.0,@aws-cdk/assets=1.59.0,@aws-cdk/aws-cloudwatch=1.59.0,@aws-cdk/aws-ec2=1.59.0,@aws-cdk/aws-events=1.59.0,@aws-cdk/aws-iam=1.59.0,@aws-cdk/aws-kms=1.59.0,@aws-cdk/aws-logs=1.59.0,@aws-cdk/aws-s3=1.59.0,@aws-cdk/aws-s3-assets=1.59.0,@aws-cdk/aws-ssm=1.59.0,@aws-cdk/cloud-assembly-schema=1.54.0,@aws-cdk/core=1.54.0,@aws-cdk/cx-api=1.54.0,@aws-cdk/region-info=1.59.0,jsii-runtime=Python/3.8.2
CloudFormationでCDK実行環境を作成します。(AWS アカウント、リージョン単位で一度だけ実行するコマンドです。(リージョンで1度実行したことがあれば、2回目以降は実行する必要はありません)以下の例では--profileでスイッチロール先である自分のアカウントを指定しています。
cdk bootstrap --profile suto
確認が終わったら以下コマンドでスタック作成してみます。
(.env) ~ sage-nw % cdk deploy sage-nw --profile suto
無事スタックCOMPLETEしました。
まとめ
AWSCDKとCloudFormationのコードの長さを比較してみると、記述の短さは一目瞭然でCDKでスタックを作成した方が簡単であることがわかります。
今回は入門編という内容でVPC構築のみでした。次回の記事でSagemakerのノートブックインスタンスを構築するコードを追加してみようと思います。
<次回記事>